﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using CLUBsInterpreter.AST;
using CLUBsInterpreter.ObjectTypes;

namespace CLUBsInterpreter
{
	/**
	 * <summary>
	 * Describes a contextual analyzer for the CLUBs environment.
	 * </summary>
	 **/
	class Visitor
	{

		/**
		 * <summary>
		 * Private field containing the TextWriter to output errors through.
		 * </summary>
		 **/
		private TextWriter m_errOut;
		/**
		 * <summary>
		 * Private field containing the currently examined scope.
		 * </summary>
		 **/
		private IdentificationTable m_currentScope;
		/**
		 * <summary>
		 * Private field containing the standard environment in use.
		 * </summary>
		 **/
		private StandardEnvironment m_standardEnv;

		private LibraryNode m_library = null;

		/**
		 * <summary>
		 * The amount of errors encountered.
		 * </summary>
		 **/
		public uint Errors = 0;

		/**
		 * <summary>
		 * Constructor takes an error output TextWriter, an identification table, and a standard environment.
		 * </summary>
		 **/
		public Visitor(TextWriter errOut, IdentificationTable idTable, StandardEnvironment stdEnv)
		{
			// Set the specified error output TextWriter
			m_errOut = errOut;
			// Set the specified identification table
			m_currentScope = idTable;
			// Set the specified standard environment
			m_standardEnv = stdEnv;
			// Populate the global scope with the global variables
			PopulateTree(stdEnv.GlobalVariables, true);
		}

		/**
		 * <summary>
		 * Populates the identification table with the standard environment definitions.
		 * </summary>
		 **/
		private void PopulateTree(Dictionary<string, CLUBsObject> env, bool globalScope = false)
		{
			// Iterate over all environment string/object pairs
			foreach (KeyValuePair<string, CLUBsObject> pair in env)
			{
				// Instantiate an identification for this object
				Identification id = new Identification();
				// Set the identification type from the object
				id.IdentType = pair.Value.ObjectIdentifierType;
				// Set the identification value reference to this object
				id.Value = pair.Value;
				// Add this identification to the current scope
				m_currentScope[pair.Key] = id;

				// Check if the identification is a simple type, which would be an error
				/*if (globalScope &&
					( id.IdentType != IdentifierType.Action
					&& id.IdentType != IdentifierType.Board
					&& id.IdentType != IdentifierType.Cell
					&& id.IdentType != IdentifierType.Piece
					&& id.IdentType != IdentifierType.Player
					&& id.IdentType != IdentifierType.Round
					&& id.IdentType != IdentifierType.Turn ))
				{
					ContextError("Bad standard environment loaded");
					return;
				}*/

				// Open the scope of the definition
				m_currentScope = m_currentScope.OpenScope(pair.Key, true);

				// Add its standard environment
				PopulateTree(pair.Value.LocalVariables);

				// Close the definition scope
				m_currentScope = m_currentScope.CloseScope();
			}
		}

		public void PopulateGlobalVars()
		{
			while (m_currentScope.Parent != null)
				m_currentScope = m_currentScope.Parent;

			foreach (KeyValuePair<string,Identification> idpair in m_currentScope)
			{
				m_standardEnv.GlobalVariables[idpair.Key] = idpair.Value.Value;
			}
		}

		/**
		 * <summary>
		 * Outputs a contextual error to the error output TextWriter.
		 * </summary>
		 **/
		private void ContextError(string msg)
		{
			// Increment amount of errors
			Errors++;
			// Print error
			m_errOut.WriteLine("Contextual error: " + msg);
		}

		#region CLUBsNode
		/**
		 * <summary>
		 * Visits a root game/library node, decorating the AST node.
		 * </summary>
		 **/
		public void VisitCLUBsNode(CLUBsNode clubs)
		{
			// Visit all library nodes
			foreach (LibraryNode lib in clubs.Libraries)
			{
				lib.Parent = clubs;
				lib.Visit(this);
			}

			// Visit all definition nodes
			foreach (DefinitionNode def in clubs.Definitions)
			{
				def.Parent = clubs;
				def.Visit(this);
			}

			// Finally, if this is not a library, visit the start action node
			if (!clubs.IsLibrary)
			{
				clubs.StartAction.Parent = clubs;
				clubs.StartAction.Visit(this);
			}

			// Perform second pass
			clubs.Visit(this);
		}
		public void VisitCLUBsNodeSecond(CLUBsNode clubs)
		{
			// Visit all library nodes
			foreach (LibraryNode lib in clubs.Libraries)
			{
				lib.Visit(this);
			}

			// Visit all definition nodes
			foreach (DefinitionNode def in clubs.Definitions)
			{
				def.Visit(this);
			}

			// Finally, if this is not a library, visit the start action node
			if (!clubs.IsLibrary)
			{
				clubs.StartAction.Visit(this);
			}
		}
		#endregion

		#region LibraryNode
		/**
		 * <summary>
		 * Visits a library reference node, scanning, parsing and subsequently visiting the library.
		 * </summary>
		 **/
		public void VisitLibraryNode(LibraryNode lib)
		{
			m_library = lib;
			// Declare scanner
			Scanner scan;
			try
			{
				// Attempt to scan the specified library
				scan = new Scanner(lib.Name + ".clubs");
			}
			catch
			{
				// Report error on failure
				m_errOut.WriteLine("Failed to open '" + lib.Name + ".clubs'.");
				return;
			}
			
			// Instantiate library identification
			Identification libID = new Identification();
			// Instantiate library object
			libID.Value = new CLUBsLibrary();
			// Set identification type
			libID.IdentType = IdentifierType.Library;
			// Add the library to the identification table
			m_currentScope[lib.Name] = libID;
			m_standardEnv.GlobalVariables[lib.Name] = libID.Value;
			// Open (create) the library's scope
			m_currentScope = m_currentScope.OpenScope(lib.Name, true);

			// Instantiate the parser
			Parser pars = new Parser(scan);
			// Instantiate the AST
			pars.ast = new CLUBsNode();
			((CLUBsLibrary)libID.Value).LibraryAST = pars.ast;
			// Instantiate identification table
			pars.idt = m_currentScope;
			// Parse the library
			pars.Parse();

			// Close the library's scope
			m_currentScope = m_currentScope.CloseScope();


			// Check for parser errors
			if (pars.errors.count > 0)
			{
				// Report error
				m_errOut.WriteLine("Parsing of library '" + lib.Name + "' failed.");
				// Remove library object from identification table
				m_currentScope.Remove(lib.Name);
				// Remove library scope from identification table
				m_currentScope.SubScopes.Remove(lib.Name);
				// Return
				return;
			}

			// Open the library's scope
			m_currentScope = m_currentScope.OpenScope(lib.Name);
			// Check whether it is in fact a library
			if (pars.ast.IsLibrary)
			{
				// Perform contextual analysis on the library
				pars.ast.Visit(this);
				/*foreach(DefinitionNode def in pars.ast.Definitions)
				{
					libID.Value.LocalVariables.Declare(def.Name, def.ObjectReference);
				}*/
			}
			// If it's not really a library
			else
			{
				// Report an error
				ContextError("Invalid library reference.");
			}
			// Close the library's scope
			m_currentScope = m_currentScope.CloseScope();

			m_library = null;
		}
		public void VisitLibraryNodeSecond(LibraryNode lib)
		{
			/*m_library = lib;

			// Open the library's scope
			m_currentScope = m_currentScope.OpenScope(lib.Name);

			CLUBsLibrary libObj = (CLUBsLibrary)m_standardEnv.GlobalVariables[lib.Name];
			CLUBsNode libAST = libObj.LibraryAST;
			if (libAST.IsLibrary)
			{
				// Perform contextual analysis on the library
				libAST.Visit(this);
			}
			// Close the library's scope
			m_currentScope = m_currentScope.CloseScope();

			m_library = null;*/
		}
		#endregion

		#region DefinitionNode
		/**
		 * <summary>
		 * Visits a definition node, decorating the AST node.
		 * </summary>
		 **/
		public void VisitDefinitionNode(DefinitionNode def)
		{
			m_standardEnv.CurrentBlock = def;

			if (m_library == null)
				m_standardEnv.GlobalVariables[def.BlockName] = def.ObjectReference;
			else
				m_standardEnv.GlobalVariables[m_library.Name].LocalVariables.Declare(def.BlockName, def.ObjectReference);
			def.InternalScope = def.ObjectReference.LocalVariables;
			def.ObjectReference.Name = def.Name;
			
			if (def is ActionDefNode)
			{
				ActionDefNode actDef = (ActionDefNode)def;
				CLUBsAction actObj = (CLUBsAction)def.ObjectReference;

				foreach (KeyValuePair<string, IdentifierType> param in actObj.ParameterTypes)
				{
					actObj.LocalVariables.Declare(param.Key, CLUBsObject.GenerateSimpleObject(param.Value));
				}

				if (actObj.ReturnType != IdentifierType.Unset)
				{
					Dictionary<string, IdentifierType> retParams = new Dictionary<string, IdentifierType>();
					retParams["retval"] = actObj.ReturnType;
					actObj.LocalVariables.Declare("give", new CLUBsAction(paramTypes: retParams));
					actObj.LocalVariables["give"].TranslatedMethod += StandardEnvironment.Instance.Action_give;
				}
				else
				{
					actObj.LocalVariables.Remove("give");
				}
			}

			// Open the definition scope
			m_currentScope = m_currentScope.OpenScope(def.BlockName);
			// Initiate the standard environment for this type of definition
			PopulateTree(def.ObjectReference.LocalVariables);
			// Close the definition scope
			m_currentScope = m_currentScope.CloseScope();
			m_standardEnv.CurrentBlock = null;

			// Visit the inner block
			VisitBlockNode(def);

		}
		public void VisitDefinitionNodeSecond(DefinitionNode def)
		{
			// Visit the inner block
			VisitBlockNodeSecond(def);
			// Add the block node's translated method to the CLUBsObject's translated method
			def.ObjectReference.TranslatedMethod = def.TranslatedMethod;
			def.ObjectReference.ReturnMethod = def.ReturnMethod;

			m_standardEnv.CurrentBlock = def;

			// If the definition was not an action, a round or a turn,
			// we execute the body immediately to perform assignments and action calls.
			if (def.DefinitionType != IdentifierType.Action
				&& def.DefinitionType != IdentifierType.Round
				&& def.DefinitionType != IdentifierType.Turn)
			{
				def.ObjectReference.Execute();
			}

			m_standardEnv.CurrentBlock = null;
		}
		#endregion

		#region BlockNode
		/**
		 * <summary>
		 * Visits a block node and all contained statements, decorating the AST.
		 * </summary>
		 **/
		public void VisitBlockNode(BlockNode block)
		{
			m_standardEnv.BlockReturnStack.Push(m_standardEnv.CurrentBlock);
			m_standardEnv.CurrentBlock = block;

			// Open the block's scope
			m_currentScope = m_currentScope.OpenScope(block);

			// Iterate over all statements in the block
			foreach (StatementNode stmt in block.Statements)
			{
				stmt.Parent = block;
				//m_errOut.WriteLine("Visiting statement on line " + stmt.LineNumber + ": " + stmt.GetType());
				// Visit the statement
				stmt.Visit(this);
			}

			// Close the block's scope
			m_currentScope = m_currentScope.CloseScope();
			m_standardEnv.CurrentBlock = m_standardEnv.BlockReturnStack.Pop();
		}
		public void VisitBlockNodeSecond(BlockNode block)
		{
			m_standardEnv.BlockReturnStack.Push(m_standardEnv.CurrentBlock);
			m_standardEnv.CurrentBlock = block;
			// Open the block's scope
			m_currentScope = m_currentScope.OpenScope(block);

			block.TranslatedMethod += delegate()
			{
				m_standardEnv.BlockReturnStack.Push(m_standardEnv.CurrentBlock);
				m_standardEnv.CurrentBlock = block;
			};

			// Iterate over all statements in the block
			foreach (StatementNode stmt in block.Statements)
			{
				//m_errOut.WriteLine("Visiting statement on line " + stmt.LineNumber + ": " + stmt.GetType());
				// Visit the statement
				stmt.Visit(this);
				// Add the statement's translated method to the block's translated method
				/*block.TranslatedMethod += delegate()
				{
					Console.WriteLine("Performing "+stmt.TranslatedMethod.Method);
				};*/
				block.TranslatedMethod += stmt.TranslatedMethod;
			}

			block.ReturnMethod += delegate()
			{
				m_standardEnv.CurrentBlock = m_standardEnv.BlockReturnStack.Pop();
			};

			// Close the block's scope
			m_currentScope = m_currentScope.CloseScope();
			m_standardEnv.CurrentBlock = m_standardEnv.BlockReturnStack.Pop();
		}
		#endregion

		#region StatementNodes
		/**
		 * <summary>
		 * Visits an assignment statement node, decorating the AST, translating the statement to native C# code.
		 * </summary>
		 **/
		public void VisitAssignStmtNode(AssignStmtNode stmt)
		{
			stmt.Ident.Parent = stmt;

			// If the left-hand part of the assignment is a declaration, visit the declaration
			if (stmt.Declaration != null)
			{
				stmt.Declaration.Parent = stmt;
				stmt.Declaration.Visit(this);
				stmt.Ident = stmt.Declaration.Ident;
			}
			// If it is just an identifier, visit that
			else
			{
				stmt.Ident.Visit(this);
			}
			// Visit the expression
			stmt.Expression.Parent = stmt;
			stmt.Expression.Visit(this);
		}
		public void VisitAssignStmtNodeSecond(AssignStmtNode stmt)
		{
			// If the left-hand part of the assignment is a declaration, visit the declaration
			if (stmt.Declaration != null)
			{
				stmt.Declaration.Visit(this);
			}
			// If it is just an identifier, visit that
			else stmt.Ident.Visit(this);

			// Visit the expression
			stmt.Expression.Visit(this);

			// Check that the expression had the same type as the identifier
			if (stmt.Ident.InferredType != stmt.Expression.InferredType &&
				stmt.Ident.InferredType != IdentifierType.Unset)
			{
				// If not, report an error
				ContextError("Invalid expression type for assignment at line " + stmt.LineNumber);
				ContextError(stmt.Ident + " is " + stmt.Ident.InferredType + " while expression is " + stmt.Expression.InferredType);
			}

			// Create the translated method for this assignment
			if (stmt.Ident.InferredType == IdentifierType.Boolean
				|| stmt.Ident.InferredType == IdentifierType.Integer
				|| stmt.Ident.InferredType == IdentifierType.String)
			{
				stmt.TranslatedMethod += delegate()
				{
					// Copy the value from the expression result into the identifier's CLUBsObject
					//stmt.Ident.Reference = CLUBsObject.GenerateSimpleObject(stmt.Expression.InferredType);
					//if (stmt.Ident.Reference is CLUBsNull)
					//Console.WriteLine("Assigning value " + stmt.Expression.GetValueReference() + " to var " + stmt.Ident);
					CLUBsObject cor = null;
					try
					{
						cor = stmt.Ident.Reference;
					}
					catch (KeyNotFoundException)
					{
						stmt.Ident.Reference = CLUBsObject.GenerateSimpleObject(stmt.Expression.InferredType);
						cor = stmt.Ident.Reference;
					}
					
					cor.CopyValueFromObject(stmt.Expression.GetValueReference());
					//stmt.Ident.Reference = stmt.Expression.GetValueReference();
				};
			}
			else
			{
				stmt.TranslatedMethod += delegate()
				{
					// Copy the reference from the expression result into the identifier's CLUBsObject reference
					stmt.Ident.Reference = stmt.Expression.GetValueReference();
				};
			}
		}

		/**
		 * <summary>
		 * Visits a declaration statement node, decorating the AST.
		 * </summary>
		 **/
		public void VisitDeclStmtNode(DeclStmtNode stmt)
		{
			// Visit the identifier
			stmt.Ident.Parent = stmt;
			stmt.Ident.SetDeclaration(true);
			stmt.Ident.Visit(this);

			stmt.Ident.Reference = CLUBsObject.GenerateSimpleObject(stmt.Ident.InferredType);
		}
		public void VisitDeclStmtNodeSecond(DeclStmtNode stmt)
		{
			// Visit the identifier
			stmt.Ident.Visit(this);
		}

		/**
		 * <summary>
		 * Visits a call statement node, decorating the AST, translating the call to native C# code.
		 * </summary>
		 **/
		public void VisitCallStmtNode(CallStmtNode stmt)
		{
			// Visit the call identifier
			stmt.Ident.Parent = stmt;
			stmt.Ident.Visit(this);
		}
		public void VisitCallStmtNodeSecond(CallStmtNode stmt)
		{
			// Visit the call identifier
			stmt.Ident.Visit(this);

			// Check that the identifier descibes a callable entity
			if (stmt.Ident.InferredType != IdentifierType.Action)
			{
				// If not, report an error
				ContextError("Action does not exist at line " + stmt.LineNumber + ": " + stmt.Ident);
				return;
			}

			// Set the node's ActionObject reference
			stmt.ActionObject = (CLUBsAction)stmt.Ident.Reference;

			// Check that the parameters correspond with the ones accepted by the callable entity
			if (stmt.Parameters.Count != stmt.ActionObject.ParameterTypes.Count)
			{
				// If not, report an error
				ContextError("Wrong parameter count for action " + stmt.Ident + " at line " + stmt.LineNumber);
				return;
			}

			// Iterate over the call parameters
			int paramIndex = 0;
			foreach (ExpressionNode exp in stmt.Parameters)
			{
				// Visit the expressions
				exp.Visit(this);

				// check that the expression type matches the expected parameter type
				if (exp.InferredType != IdentifierType.Unset &&
					stmt.ActionObject.ParameterTypes.ElementAt(paramIndex).Value != IdentifierType.Unset &&
					exp.InferredType != stmt.ActionObject.ParameterTypes.ElementAt(paramIndex).Value)
				{
					// If not, report an error
					ContextError("Action at line " + stmt.LineNumber + " has some invalid arguments");
					return;
				}

				// Increment parameter index
				paramIndex++;
			}

			// Decorate node with ReturnType
			stmt.ReturnType = stmt.ActionObject.ReturnType;

			// Decorate statement with TranslatedMethod
			stmt.TranslatedMethod += delegate()
			{
				// Initiate array of arguments of appropriate size
				CLUBsObject[] args = new CLUBsObject[stmt.Parameters.Count];

				// Iterate over all parameter expressions
				int i = 0;
				foreach (ExpressionNode expr in stmt.Parameters)
				{
					// Evaluate the value of the expression and add it to the parameter array
					args[i] = expr.GetValueReference();
					i++;
				}

				// Call the action
				m_standardEnv.CallAction(stmt.ActionObject, args);
			};

		}

		/**
		 * <summary>
		 * Visits an if statement node, decorating the AST, translating the control structure to native C# code.
		 * </summary>
		 **/
		public void VisitIfStmtNode(IfStmtNode stmt)
		{
			// Visit the condition expression
			stmt.Condition.Parent = stmt;
			stmt.Condition.Visit(this);
			// Visit the body block
			stmt.Body.Parent = stmt;
			stmt.Body.InternalScope.Parent = stmt.ParentBlock.InternalScope;
			stmt.Body.Visit(this);
		}
		public void VisitIfStmtNodeSecond(IfStmtNode stmt)
		{
			// Visit the condition expression
			stmt.Condition.Visit(this);
			// Check that the condition is a boolean expression
			if (stmt.Condition.InferredType != IdentifierType.Boolean && stmt.Condition.InferredType != IdentifierType.Unset)
			{
				// If not, report an error
				ContextError("Bad condition for if statement at line " + stmt.LineNumber + " (type was "+ stmt.Condition.InferredType +")");
				return;
			}
			// Visit the body block
			stmt.Body.Visit(this);

			// Decorate statement with translated method
			stmt.TranslatedMethod += delegate()
			{
				// Check that the condition expression evaluates to true
				if ((CLUBsBoolean)stmt.Condition.GetValueReference())
				{
					// In which case, invoke the body
					CLUBsObject.Execute(stmt.Body.TranslatedMethod, stmt.Body.ReturnMethod);
				}
			};
		}

		/**
		 * <summary>
		 * Visits a check statement node, decorating the AST, translating the control structure to native C# code.
		 * </summary>
		 **/
		public void VisitCheckStmtNode(CheckStmtNode stmt)
		{
			// Visit the checked expression
			stmt.CheckedExpression.Parent = stmt;
			stmt.CheckedExpression.Visit(this);
			// Iterate over all case nodes
			foreach (CaseNode cn in stmt.Cases)
			{
				// Visit the case node
				cn.Parent = stmt;
				cn.InternalScope.Parent = stmt.ParentBlock.InternalScope;
				cn.Visit(this);
			}
		}
		public void VisitCheckStmtNodeSecond(CheckStmtNode stmt)
		{
			// Visit the checked expression
			stmt.CheckedExpression.Visit(this);
			// Iterate over all case nodes
			foreach (CaseNode cn in stmt.Cases)
			{
				// Visit the case node
				cn.Visit(this);
				// Check that the match expression matches the type of the checked expression
				if (stmt.CheckedExpression.InferredType != cn.MatchExpression.InferredType)
				{
					// If not, report an error
					ContextError("Wrong expression type in case at line " + cn.LineNumber);
				}
			}

			// TODO: CREATE TRANSLATED METHOD warning
			stmt.TranslatedMethod += delegate()
			{
				foreach (CaseNode theCase in stmt.Cases)
				{
					if (theCase.MatchExpression.InferredType == stmt.CheckedExpression.InferredType &&
						theCase.MatchExpression.GetValueReference() == stmt.CheckedExpression.GetValueReference())
					{
						CLUBsObject.Execute(theCase.TranslatedMethod, theCase.ReturnMethod);
						return;
					}
				}
			};

		}

		/**
		 * <summary>
		 * Visits a case node, decorating the AST, translating the case to native C# code.
		 * </summary>
		 **/
		public void VisitCaseNode(CaseNode caseNode)
		{
			// Visit the match expression
			caseNode.MatchExpression.Parent = caseNode;
			caseNode.MatchExpression.Visit(this);
			// Visit the block
			VisitBlockNode(caseNode);

			// CREATE TRANSLATED METHOD will be done by VisitBlockNode
		}
		public void VisitCaseNodeSecond(CaseNode caseNode)
		{
			// Visit the match expression
			caseNode.MatchExpression.Visit(this);
			// Visit the block
			VisitBlockNodeSecond(caseNode);

			// CREATE TRANSLATED METHOD will be done by VisitBlockNode
		}

		/**
		 * <summary>
		 * Visits a while statement node, decorating the AST, translating the control structure to native C# code.
		 * </summary>
		 **/
		public void VisitWhileStmtNode(WhileStmtNode stmt)
		{
			// Visit the condition expression
			stmt.Condition.Parent = stmt;
			stmt.Condition.Visit(this);
			// Visit the body block
			stmt.Body.Parent = stmt;
			stmt.Body.InternalScope.Parent = stmt.ParentBlock.InternalScope;
			stmt.Body.Visit(this);
		}
		public void VisitWhileStmtNodeSecond(WhileStmtNode stmt)
		{
			// Visit the condition expression
			stmt.Condition.Visit(this);
			// Check that the condition is a boolean expression
			if (stmt.Condition.InferredType != IdentifierType.Boolean)
			{
				// If not, report an error
				ContextError("Bad condition for while statement at line " + stmt.LineNumber);
				return;
			}
			// Visit the body block
			stmt.Body.Visit(this);

			// TODO: CREATE TRANSLATED METHOD done
			stmt.TranslatedMethod += delegate()
			{
				while ((CLUBsBoolean)stmt.Condition.GetValueReference() && !m_standardEnv.ExecutionReturnTriggered)
				{
					CLUBsObject.Execute(stmt.Body.TranslatedMethod, stmt.Body.ReturnMethod);
				}
			};
		}

		/**
		 * <summary>
		 * Visits a for statemtnt node, decorating the AST, translating the control structure to native C# code.
		 * </summary>
		 **/
		public void VisitForStmtNode(ForStmtNode stmt)
		{
			// Visit the initializer statement
			stmt.Initialize.Parent = stmt;
			stmt.Initialize.Visit(this);
			// Visit the condition statement
			stmt.Condition.Parent = stmt;
			stmt.Condition.Visit(this);
			// Visit the incrementer statement
			stmt.Increment.Parent = stmt;
			stmt.Increment.Visit(this);

			// Visit the body block
			stmt.Body.Parent = stmt;
			stmt.Body.InternalScope.Parent = stmt.ParentBlock.InternalScope;
			stmt.Body.Visit(this);
		}
		public void VisitForStmtNodeSecond(ForStmtNode stmt)
		{
			// Visit the initializer statement
			stmt.Initialize.Visit(this);
			// Visit the condition statement
			stmt.Condition.Visit(this);
			// Check that the condition is a boolean expression
			if (stmt.Condition.InferredType != IdentifierType.Boolean)
			{
				// If not, report an error
				ContextError("Bad condition in for statement at line " + stmt.LineNumber);
			}
			// Visit the incrementer statement
			stmt.Increment.Visit(this);

			// Visit the body block
			stmt.Body.Visit(this);

			// TODO: CREATE TRANSLATED METHOD done
			stmt.TranslatedMethod += delegate()
			{
				for (stmt.Initialize.TranslatedMethod(); (CLUBsBoolean)stmt.Condition.GetValueReference() && !m_standardEnv.ExecutionReturnTriggered; stmt.Increment.TranslatedMethod())
				{
					CLUBsObject.Execute(stmt.Body.TranslatedMethod, stmt.Body.ReturnMethod);
				}
			};
		}
		#endregion

		#region ExpressionNodes
		/**
		 * <summary>
		 * Visits a binary expression node, decorating the AST, translating the expression to native C# code.
		 * </summary>
		 **/
		public void VisitBinaryExpNode(BinaryExpNode exp)
		{
			// Visit the left-hand expression
			exp.Exp1.Parent = exp;
			exp.Exp1.Visit(this);
			// Visit the right-hand expression
			exp.Exp2.Parent = exp;
			exp.Exp2.Visit(this);
			// Set the expression type to boolean if the "is" operator is being applied
			if (exp.Op == Operator.Is)
				exp.InferredType = IdentifierType.Boolean;
			// Else, set the expression type to that of the left-hand expression
			else
				exp.InferredType = exp.Exp1.InferredType;
		}
		public void VisitBinaryExpNodeSecond(BinaryExpNode exp)
		{
			// Visit the left-hand expression
			exp.Exp1.Visit(this);
			// Visit the right-hand expression
			exp.Exp2.Visit(this);
			// Check for bad operator
			if (exp.Op == Operator.Invalid || exp.Op == Operator.Not)
			{
				ContextError("Bad operator in expression at line " + exp.LineNumber);
				return;
			}
			// Check for expression type mismatch
			if (exp.Exp1.InferredType != exp.Exp2.InferredType &&
				exp.Exp1.InferredType != IdentifierType.Null && exp.Exp2.InferredType != IdentifierType.Null &&
				exp.Exp1.InferredType != IdentifierType.Unset && exp.Exp2.InferredType != IdentifierType.Unset)
			{
				ContextError("Incompatible types in expression at line " + exp.LineNumber + " ("+exp.Exp1.InferredType+" and "+exp.Exp2.InferredType+")");
				return;
			}
			// Check for bad operator/type combination
			if (exp.Exp1.InferredType != IdentifierType.Integer &&
				(exp.Op == Operator.Div || exp.Op == Operator.Minus || exp.Op == Operator.Mult || exp.Op == Operator.Plus))
			{
				ContextError("Incompatible type with operator at line " + exp.LineNumber);
				return;
			}
			// Set the expression type to boolean if the "is" operator is being applied
			if (exp.Op == Operator.Is)
				exp.InferredType = IdentifierType.Boolean;
			// Else, set the expression type to that of the left-hand expression
			else
				exp.InferredType = exp.Exp1.InferredType;
		}

		/**
		 * <summary>
		 * Visits a unary expression node, decorating the AST, translating the expression to native C# code.
		 * </summary>
		 **/
		public void VisitUnaryExpNode(UnaryExpNode exp)
		{
			// Visit the sub-expression
			exp.Exp.Parent = exp;
			exp.Exp.Visit(this);
			// Set the expression type to the sub-expression type
			//exp.InferredType = exp.Exp.InferredType;
			exp.InferredType = exp.Op == Operator.Minus ? IdentifierType.Integer : IdentifierType.Boolean;
		}
		public void VisitUnaryExpNodeSecond(UnaryExpNode exp)
		{
			// Visit the sub-expression
			exp.Exp.Visit(this);
			// Check if the operator is "not" and the expression is not a boolean
			if (exp.Op == Operator.Not &&
				exp.Exp.InferredType != IdentifierType.Boolean &&
				exp.Exp.InferredType != IdentifierType.Unset)
			{
				// If so, report an error
				ContextError("Bad boolean unary operator expression at line " + exp.LineNumber);
				return;
			}
			// Else, check if the operator is "minus" and the expression is not an integer
			else if (exp.Op == Operator.Minus &&
				exp.Exp.InferredType != IdentifierType.Integer &&
				exp.Exp.InferredType != IdentifierType.Unset)
			{
				// If so, report an error
				ContextError("Bad integer unary operator expression at line " + exp.LineNumber);
				return;
			}
			// Set the expression type to the sub-expression type
			//exp.InferredType = exp.Exp.InferredType;
		}

		/**
		 * <summary>
		 * Visits a call expression node, decorating the AST, translating the expression to native C# code.
		 * </summary>
		 **/
		public void VisitCallExpNode(CallExpNode exp)
		{
			// Visit the call statement node
			exp.Call.Parent = exp;
			exp.Call.Visit(this);
			// Set the expression type to the return type of the action call
			exp.InferredType = exp.Call.ReturnType;
		}
		public void VisitCallExpNodeSecond(CallExpNode exp)
		{
			// Visit the call statement node
			exp.Call.Visit(this);
			// Set the expression type to the return type of the action call
			exp.InferredType = exp.Call.ReturnType;
		}

		/**
		 * <summary>
		 * Visits a string expression node, decorating the AST.
		 * </summary>
		 **/
		public void VisitStringExpNode(StringExpNode exp)
		{
			// Set the expression type to string
			exp.InferredType = IdentifierType.String;
		}
		public void VisitStringExpNodeSecond(StringExpNode exp)
		{
		}

		/**
		 * <summary>
		 * Visits a integer expression node, decorating the AST.
		 * </summary>
		 **/
		public void VisitIntegerExpNode(IntegerExpNode exp)
		{
			// Set the expression type to integer
			exp.InferredType = IdentifierType.Integer;
		}
		public void VisitIntegerExpNodeSecond(IntegerExpNode exp)
		{
		}

		/**
		 * <summary>
		 * Visits a VName expression node, decorating the AST.
		 * </summary>
		 **/
		public void VisitVNameExpNode(VNameExpNode exp)
		{
			// Visit the identifier
			exp.Ident.Parent = exp;
			exp.Ident.Visit(this);
			// Set the expression type to the identifier's type
			exp.InferredType = exp.Ident.InferredType;
		}
		public void VisitVNameExpNodeSecond(VNameExpNode exp)
		{
			// Visit the identifier
			exp.Ident.Visit(this);
			// Set the expression type to the identifier's type
			exp.InferredType = exp.Ident.InferredType;
		}

		/**
		 * <summary>
		 * Visits a parenthesis expression node, decorating the AST, translating the expression to native C# code.
		 * </summary>
		 **/
		public void VisitParenthesisExpNode(ParenthesisExpNode exp)
		{
			// Visit the sub-expression
			exp.Exp.Parent = exp;
			exp.Exp.Visit(this);
			// Set the expression type to that of the sub-expression
			exp.InferredType = exp.Exp.InferredType;
		}
		public void VisitParenthesisExpNodeSecond(ParenthesisExpNode exp)
		{
			// Visit the sub-expression
			exp.Exp.Visit(this);
			// Set the expression type to that of the sub-expression
			exp.InferredType = exp.Exp.InferredType;
		}
		#endregion

		#region Identifiers
		/**
		 * <summary>
		 * Visits an identifier node, decorating the AST.
		 * </summary>
		 **/
		public void VisitIdentifier(Identifier ident)
		{
			// Iterate over all array identifiers in this composite identifier
			ArrayIdentifier lastArr = null;
			foreach (ArrayIdentifier arr in ident.SubIdents)
			{
				// Set the LastArrayIdentifier reference in the current array identifier
				arr.LastArrayIdentifier = lastArr;
				// Visit the array identifier
				arr.Parent = ident;
				arr.Visit(this);
				// Update the local reference to the last array identifier visited
				lastArr = arr;
			}
			// Set the type of the composite identifier to that of the last array identifier
			// (I.e., the one with the highest scope level)
			ident.InferredType = ident.SubIdents.Last().InferredType;
			// Set the CLUBsObject reference of this identifier to that of the last array identifier
			//ident.Reference = ident.SubIdents.Last().Reference;
		}
		public void VisitIdentifierSecond(Identifier ident)
		{
			// Iterate over all array identifiers in this composite identifier
			foreach (ArrayIdentifier arr in ident.SubIdents)
			{
				// Visit the array identifier
				arr.Visit(this);
				// If the array identifier's type is unset, return due to error
				if (arr.InferredType == IdentifierType.Unset)
					return;
			}
			// Set the type of the composite identifier to that of the last array identifier
			// (I.e., the one with the highest scope level)
			ident.InferredType = ident.SubIdents.Last().InferredType;
			// Set the CLUBsObject reference of this identifier to that of the last array identifier
			//ident.Reference = ident.SubIdents.Last().Reference;
		}

		/**
		 * <summary>
		 * Visits an array identifier node, decorating the AST.
		 * </summary>
		 **/
		public void VisitArrayIdentifier(ArrayIdentifier ident)
		{
			/*// Set reference to last array identifier to this one for now
			ArrayIdentifier last = ident;
			// Initiate path stack -- this will contain the scope path taken to get to the scope where this array identifier should exist
			Stack<ArrayIdentifier> path = new Stack<ArrayIdentifier>();
			// Move back through the last array identifiers
			while ((last = last.LastArrayIdentifier) != null)
			{
				// Add them to the path stack
				path.Push(last);
			}
			// Set the scope offset to the path stack size
			int scopeOffset = path.Count;
			// Iterate over all path elements
			while (path.Count > 0)
			{
				// Open scopes popping the array identifiers from the path stack
				m_currentScope = m_currentScope.OpenScope(path.Pop().Name);
			}*/
			if (ident.LastArrayIdentifier == null)
			{
				// Check that the name of the identifier exists in the current scope
				if (m_currentScope.ContainsKey(ident.Name))
				{
					// Point the idenfier reference at the object with the identifier's name
					ident.Reference = m_currentScope[ident.Name].Value;
					// Set the identifier's type to the type of the object
					ident.InferredType = m_currentScope[ident.Name].IdentType;

					//ident.TargetVarScope = m_currentScope.GetScopeOf(ident.Name);
					//if (ident.TargetVarScope == null) ident.TargetVarScope = m_standardEnv.GlobalVariables;

					// If array indices are specified, report an error, since these are not implemented
					if (ident.Indices.Count > 0)
					{
						ContextError("Arrays are not implemented: " + ident);
					}
				}

			}
			else
			{
				// IF we don't have access to the scope of the last reference
				if (ident.LastArrayIdentifier.Reference == null)
				{
					ident.InferredType = IdentifierType.Unset;
					return;
				}

				// Check that the name of the identifier exists in the current scope
				if (ident.LastArrayIdentifier.Reference.LocalVariables.ContainsKey(ident.Name))
				{
					// Point the idenfier reference at the object with the identifier's name
					ident.Reference = ident.LastArrayIdentifier.Reference.LocalVariables[ident.Name];
					// Set the identifier's type to the type of the object
					ident.InferredType = ident.LastArrayIdentifier.Reference.LocalVariables[ident.Name].ObjectIdentifierType;

					//ident.TargetVarScope = ident.LastArrayIdentifier.TargetVarScope[ident.LastArrayIdentifier.Name].LocalVariables;

					// If array indices are specified, report an error, since these are not implemented
					if (ident.Indices.Count > 0)
					{
						ContextError("Arrays are not implemented: " + ident);
					}
				}
			}
			
			/*for (; scopeOffset > 0; scopeOffset--)
			{
				m_currentScope = m_currentScope.CloseScope();
			}*/
		}
		public void VisitArrayIdentifierSecond(ArrayIdentifier ident)
		{
			/*// Set reference to last array identifier to this one for now
			ArrayIdentifier last = ident;
			// Initiate path stack -- this will contain the scope path taken to get to the scope where this array identifier should exist
			Stack<ArrayIdentifier> path = new Stack<ArrayIdentifier>();
			// Move back through the last array identifiers
			while ((last = last.LastArrayIdentifier) != null)
			{
				// Add them to the path stack
				path.Push(last);
			}
			// Set the scope offset to the path stack size
			int scopeOffset = path.Count;
			// Iterate over all path elements
			while (path.Count > 0)
			{
				// Open scopes popping the array identifiers from the path stack
				m_currentScope = m_currentScope.OpenScope(path.Pop().Name);
			}*/
			if (ident.LastArrayIdentifier == null)
			{
				// Check that the name of the identifier exists in the current scope
				if (m_currentScope.ContainsKey(ident.Name))
				{
					// Point the idenfier reference at the object with the identifier's name
					ident.Reference = m_currentScope[ident.Name].Value;
					// Set the identifier's type to the type of the object
					ident.InferredType = m_currentScope[ident.Name].IdentType;

					//ident.TargetVarScope = m_currentScope.GetScopeOf(ident.Name);
					//if (ident.TargetVarScope == null) ident.TargetVarScope = m_standardEnv.GlobalVariables;

					// If array indices are specified, report an error, since these are not implemented
					if (ident.Indices.Count > 0)
					{
						ContextError("Arrays are not implemented: " + ident);
					}
				}
				// If not, report an error
				else
				{
					ContextError("Identifier not found in scope: " + ident.Name + " at line " + ident.LineNumber);
					ident.InferredType = IdentifierType.Unset;
				}

			}
			else
			{
				// IF we don't have access to the scope of the last reference
				if (ident.LastArrayIdentifier.Reference == null)
				{
					ident.InferredType = IdentifierType.Unset;
					return;
				}

				// Check that the name of the identifier exists in the current scope
				if (ident.LastArrayIdentifier.Reference.LocalVariables.ContainsKey(ident.Name))
				{
					// Point the idenfier reference at the object with the identifier's name
					ident.Reference = ident.LastArrayIdentifier.Reference.LocalVariables[ident.Name];
					// Set the identifier's type to the type of the object
					ident.InferredType = ident.LastArrayIdentifier.Reference.LocalVariables[ident.Name].ObjectIdentifierType;

					//ident.TargetVarScope = ident.LastArrayIdentifier.TargetVarScope[ident.LastArrayIdentifier.Name].LocalVariables;

					// If array indices are specified, report an error, since these are not implemented
					if (ident.Indices.Count > 0)
					{
						ContextError("Arrays are not implemented: " + ident);
					}
				}
				// If not, report an error
				/*else
				{
					ContextError("Identifier not found in scope: " + ident.Name + " at line " + ident.LineNumber);
					ident.InferredType = IdentifierType.Unset;
				}*/
			}
			/*for (; scopeOffset > 0; scopeOffset--)
			{
				m_currentScope = m_currentScope.CloseScope();
			}*/
		}
		#endregion

	}
}